package com.eastedu.util;

import com.eastedu.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 袁松明
 */
@Slf4j
public class FileUtils {
    private static final String SYSTEM_TMP_DIR = System.getProperty("java.io.tmpdir");
    private static final String FILE_EXTENSION_SPLIT_SYMBOL = ".";

    private FileUtils() {
    }

    /**
     * 获取指定文件夹下所有文件
     *
     * @param path 文件夹路径
     * @return 文件路径列表
     */
    public static List<Path> getFiles(Path path) {
        List<Path> paths = new ArrayList<>();
        if (!Files.exists(path)) {
            return paths;
        }
        try {
            Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    paths.add(file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
                    return FileVisitResult.CONTINUE;
                }
            });
            return paths;
        } catch (Exception e) {
            throw new ServiceException(e, "读取目录下文件列表异常: " + e.getMessage());
        }
    }

    /**
     * 创建目录
     * 如果目标路径存在，则删除目标路径并重建
     *
     * @param path 目标路径
     */
    public static void createPath(Path path) {
        try {
            deletePath(path);
            // 注意，此处未考虑创建父级文件夹
            Files.createDirectory(path);
        } catch (Exception e) {
            throw new ServiceException(e, "创建工作目录异常: " + e.getMessage());
        }
    }

    /**
     * 创建目录
     * 如果目标路径存在，则删除目标路径并重建
     *
     * @param path 目标路径
     */
    public static void deletePath(Path path) {
        log.info("正在删除目标文件夹：{}", path);
        try {
            if (Files.exists(path)) {
                Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                        Files.delete(file);
                        return FileVisitResult.CONTINUE;
                    }

                    @Override
                    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                        Files.delete(dir);
                        return FileVisitResult.CONTINUE;
                    }
                });
                Files.deleteIfExists(path);
            }
        } catch (Exception e) {
            throw new ServiceException(e, "删除工作目录异常: " + e.getMessage());
        }
    }

    /**
     * 生成工作目录
     *
     * @param workdir 工作目录
     * @return 工作目录
     */
    public static Path buildWorkdir(String workdir) {
        Path path = Paths.get(SYSTEM_TMP_DIR, workdir);
        try {
            createPath(path);
        } catch (Exception e) {
            throw new ServiceException(e, "创建临时工作目录异常: " + e.getMessage());
        }
        return path;
    }

    /**
     * 下载远程文件到本地
     *
     * @param downloadUrl 下载地址
     * @param md5         文件MD5
     * @param extension   文件扩展名
     * @param path        本地文件位置
     */
    public static Path downloadFile(String downloadUrl, String md5, String extension, Path path) {
        String targetFileName = md5 + FILE_EXTENSION_SPLIT_SYMBOL + extension;
        Path targetFilePath = Paths.get(path.toString(), targetFileName);
        try (InputStream inputStream = new URL(downloadUrl).openStream()) {
            Files.copy(inputStream, targetFilePath, StandardCopyOption.REPLACE_EXISTING);
        } catch (Exception e) {
            throw new ServiceException(e, "下载原始文件失败: " + e.getMessage());
        }
        /// 在开发环境发现部分文件下载完成后MD5匹配失败，咨询PM，不校验下载的文件MD5，导致的风险无法预料
        /// String targetFileMd5 = calcMd5(targetFilePath);
        /// if (!targetFileMd5.equalsIgnoreCase(md5)) {
        ///     throw new InvalidContentException("下载文件失败", "原始文件MD5校验不通过", md5);
        /// }
        return targetFilePath;
    }

    /**
     * 计算文件MD5
     *
     * @param path 文件路径
     * @return MD5
     */
    public static String calcMd5(Path path) {
        try (InputStream inputStream = Files.newInputStream(path)) {
            return DigestUtils.md5Hex(inputStream);
        } catch (Exception e) {
            throw new ServiceException(e, "计算文件md5失败: " + e.getMessage());
        }
    }

    /**
     * 计算文件大小
     *
     * @param path 文件路径
     * @return 大小
     */
    public static Long calcSize(Path path) {
        try {
            return Files.size(path);
        } catch (Exception e) {
            throw new ServiceException(e, "计算文件大小失败: " + e.getMessage());
        }
    }

    /**
     * 生成HLS向量
     *
     * @return 向量
     */
    public static String generateHlsVector() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] randomBytes = new byte[16];
        secureRandom.nextBytes(randomBytes);
        StringBuilder hexString = new StringBuilder(32);
        for (byte b : randomBytes) {
            hexString.append(String.format("%02x", b));
        }
        return hexString.toString();
    }

    /**
     * 生成HLS密钥文件并写入本地
     *
     * @param path 写入文件路径
     * @return HLS密钥文件二进制
     */
    public static byte[] generateHlsKeyFile(Path path) {
        SecureRandom secureRandom = new SecureRandom();
        byte[] randomBytes = new byte[16];
        secureRandom.nextBytes(randomBytes);
        try (FileOutputStream fileOutputStream = new FileOutputStream(path.toString())) {
            fileOutputStream.write(randomBytes);
        } catch (Exception e) {
            throw new ServiceException(e, "HLS密钥生成失败: " + e.getMessage());
        }
        return randomBytes;
    }

    /**
     * 生成HLS KeyInfo文件并写入本地
     *
     * @param hlsVector      向量
     * @param hlsKeyFile     密钥文件位置
     * @param hlsKeyInfoFile KeyInfo文件位置
     */
    public static void createKeyInfo(String hlsVector, Path hlsKeyFile, Path hlsKeyInfoFile) {
        String hlsKeyFileName = hlsKeyFile.getFileName().toString();
        String hlsKeyFileFullPath = hlsKeyFile.toString();
        StringBuilder fileContent = new StringBuilder();
        fileContent.append(hlsKeyFileName).append("\n");
        fileContent.append(hlsKeyFileFullPath).append("\n");
        fileContent.append(hlsVector).append("\n");
        try (FileOutputStream fos = new FileOutputStream(hlsKeyInfoFile.toString())) {
            fos.write(fileContent.toString().getBytes());
        } catch (Exception e) {
            throw new ServiceException(e, "生成HLS KeyInfo文件失败: " + e.getMessage());
        }
    }

    /**
     * 获取不含扩展名的文件名
     *
     * @param fileName 文件名
     * @return 文件名
     */
    public static String getFileNameWithoutSuffix(String fileName) {
        return fileName.substring(0, fileName.lastIndexOf("."));
    }
}
